home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 220 / 220.xpi / chrome / flashgot.jar / content / flashgot / MediaSniffer.js < prev    next >
Encoding:
JavaScript  |  2010-01-24  |  9.3 KB  |  233 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2009 Giorgio Maone - g.maone@informaction.com
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.                              
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. var MediaSniffer = {
  23.  
  24.   QueryInterface: xpcom_generateQI([
  25.     CI.nsIObserver, 
  26.     CI.nsISupportsWeakReference,
  27.     CI.nsISupports,
  28.     CI.nsIWebProgressListener
  29.   ]),
  30.   
  31.   
  32.   debug: false,
  33.   // http-on-examine-response Observer
  34.   
  35.   mimeService: CC['@mozilla.org/uriloader/external-helper-app-service;1']
  36.                      .getService(CI.nsIMIMEService),
  37.   mediaTypesRx: /\b(?:audio|video|smil)\b/i,
  38.   mediaMap: {
  39.     "asx": "video/x-ms-asx", // fake, see "media" processor
  40.     "flv": "video/flv", // flv is not mapped by MimeService
  41.     "f4v": "video/mp4",
  42.     "f4a": "video/mp4",
  43.     "f4b": "video/mp4",
  44.     "f4p": "video/mp4",
  45.     "mp3": "audio/mp3", // MimeService chokes on this
  46.     "mp4": "video/mp4" // just to be sure :)
  47.   },
  48.   get inverseMediaMap() {
  49.     var m = {};
  50.     for (var p in this.mediaMap) m[this.mediaMap[p]] = p;
  51.     delete this.inverseMediaMap;
  52.     return this.inverseMediaMap = m;
  53.   },
  54.   
  55.   sniffType: function(channel, forcedContentType) {
  56.     var path;
  57.     if (channel instanceof CI.nsIHttpChannel) {
  58.       try {
  59.         path = channel.getResponseHeader("content-disposition").match(/; filename="([^"]+)/i)[1];
  60.       } catch(e) {}
  61.      }
  62.     if (!path) path = channel.URI.path;
  63.     path = path.replace(/#[\s\S]*/, '');
  64.     if (path.length > 1024) path = path.substring(0, 1024);
  65.     const extFinder = /([^"\/]+\.(\w{2,5}))(?=[\?&]|$)/g;
  66.     extFinder.lastIndex = 0;
  67.     var m, ext, contentType, ms;
  68.     while((m = extFinder.exec(path))) {
  69.       ext = m[2];
  70.       if ((contentType = this.mediaMap[ext])) break;
  71.       try {
  72.         contentType = this.mimeService.getTypeFromExtension(ext);
  73.         if (this.mediaTypesRx.test(contentType)) break;
  74.       } catch(e) {}
  75.     }
  76.     
  77.     var fname;
  78.     if (forcedContentType && !(forcedContentType == "video/x-ms-asf" && contentType == "video/x-ms-asx")) {
  79.       var fname = m && m[1];
  80.       if (!fname) {
  81.         fname = (channel.URI instanceof CI.nsIURL) && channel.URI.fileName || path.replace(/.*\//g, '');
  82.         try {
  83.           fname += "." + (this.inverseMediaMap[forcedContentType] || this.mimeService.getPrimaryExtension(forcedContentType, '')); 
  84.         } catch(ex) {}
  85.       }
  86.       return { fname: fname, contentType: forcedContentType };
  87.     }
  88.     
  89.     return m && { fname: m[1], contentType: contentType };
  90.   },
  91.   
  92.   
  93.   
  94.   observe: function(channel, topic, data) {
  95.     if (channel instanceof CI.nsIChannel) {
  96.       try {
  97.         var contentType = channel.contentType;
  98.         if (!contentType || /\b(?:x?html|image|css|javascript|shockwave)\b/i.test(contentType)) return;
  99.          
  100.         if (this.debug) dump("Examining " + channel.URI.spec + " (" + contentType + ")\n");
  101.         
  102.         var typeInfo = null;
  103.         if (this.mediaTypesRx.test(contentType) || (typeInfo = this.sniffType(channel))) {
  104.  
  105.           var win = (DOM.findChannelWindow(channel) || DOM.mostRecentBrowserWindow.content).top;
  106.  
  107.           if (this.debug) dump("Media Window: " + win + " - " + win.location.href + " -- " + contentType + "\n");
  108.           var media = win._flashgotMedia || (win._flashgotMedia = []);
  109.           var url = channel.URI.spec;
  110.           var map = media._map || (media._map = {});
  111.           
  112.           if (!(url in map)) {
  113.             
  114.             contentType = contentType.replace(/;.*/); // ignore trailing extras, e.g. charset
  115.             
  116.             if (/\bx-ms-asf\b/.test(contentType)) {
  117.               try {
  118.                 if (channel.contentLength && channel.contentLength < 2048) contentType = "video/x-ms-asx";
  119.               } catch(e) {}
  120.               try {
  121.                 if (channel.contentCharset && channel.contentLength < 16384) contentType = "video/x-ms-asx";
  122.               } catch(e) {}
  123.             }
  124.             
  125.             // asf content type can also refer to an asx, we need to check the file name to decide
  126.             if (!typeInfo) {
  127.               typeInfo = this.sniffType(channel, contentType)
  128.             }
  129.             
  130.             contentType = typeInfo.contentType;
  131.             
  132.             var contentLength = -1;
  133.             try {
  134.                 contentLength = channel.contentLength;
  135.             } catch (e) {}
  136.             
  137.             var tip = url.match(/[^\/]*$/)[0] || '';
  138.             if (tip) {
  139.                 tip = contentType + ": " + tip;
  140.                 if (tip.length > 60) {
  141.                     tip = tip.substring(0, 29) + "..." + tip.slice(-28); 
  142.                 }
  143.             } else tip = contentType;
  144.             
  145.             
  146.             // Youtube channel hack
  147.             var doc = win.document;
  148.             var node = doc.getElementById("playnav-curvideo-title");
  149.             
  150.             var title = (node && node.textContent || win.document.title).replace(/^\s+|\s+$/, '') || '';
  151.             const unicode = fg.getPref("media.unicode") && /^UTF.?8$/i.test(win.document.characterSet);
  152.             if (title) {
  153.               // remove site name from title
  154.               title = title.replace(new RegExp("\\b(?:" +
  155.                   (win.location.host || '').split(".").filter(function(s) { return s }).join("|")  + ")\\b", 'ig'), '')
  156.                   .replace(/https?:\/{2}/gi, '').replace(unicode ? /^[^\w\u0080-\uffff]*(.*?)[^\w\u0080-\uffff]*$/g : /^\W*(.*?)\W*$/g, '$1').replace(unicode ? /[\u0000-\u0020]+/g : /\W+/g, '_')
  157.             }
  158.             
  159.             var fname = title && fg.getPref("media.guessName", true) && (title + "_" + typeInfo.fname)
  160.               .replace(unicode ? /[^\w\.\u0080-\uffff]+/g : /[^\w\.]+/g, '_').replace(/_(?:get_video|videoplayback)\b/, '')
  161.                         || '';
  162.           
  163.             var redirect;
  164.             var host = (channel.originalURI || channel.URI).host;
  165.             if (host) {
  166.               host = host.replace(/\./g, '_');
  167.               while(host && (redirect = fg.getPref("media.redirect." + host, -1)) == -1) {
  168.                 host = host.replace(/.*?(?:_|$)/, '');
  169.               }
  170.             }
  171.             if (redirect == -1) redirect = fg.getPref("media.redirect", 0); // 0 - no redirect, 1 redirect, 2 include both initial and final url
  172.             if (redirect == 0) url = channel.originalURI && channel.originalURI.spec || url;
  173.             
  174.             
  175.             var size = contentLength < 0 ? "???KB"
  176.               : contentLength < 1024 ? (contentLength + "B")
  177.                   : contentLength < 1048576 ? (Math.round(contentLength  / 1024) + "KB")
  178.                     : (Math.round(contentLength / 1048576)) + "MB";
  179.             
  180.             
  181.             var label = (fname || tip) + " (" + size + ")";
  182.             var description = title + " (" + contentType + ", " + size + ")";
  183.             tip += " (" + size + ")";
  184.             
  185.             while(url) {
  186.               media.push (map[url] = {
  187.                 href: (fname && fg.getPref("media.forceNameHack", true) && url.indexOf("#") == -1) ? url + "#/" + encodeURIComponent(fname) : url,
  188.                 referrer: (channel instanceof CI.nsIHttpChannel) && channel.referrer && channel.referrer.spec,
  189.                 description: title + " (" + contentType + ")",
  190.                 contentType: contentType,
  191.                 contentLength: contentLength,
  192.                 label: label,
  193.                 tip: tip,
  194.                 fname: fname
  195.               });
  196.               
  197.               url = redirect == 2 && (!channel.originalURI || channel.originalURI.spec == url ? null : channel.originalURI.spec);
  198.             }
  199.           }
  200.  
  201.           var bw = DOM.mostRecentBrowserWindow;
  202.           if (bw && bw.gFlashGot) bw.gFlashGot.updateMediaUI();
  203.           
  204.           if (this.debug) dump(win._flashgotMedia && win._flashgotMedia.toSource() + "\n");
  205.         }
  206.       } catch(e) {
  207.         var msg = topic + " " + e.toString();
  208.         if (channel) {
  209.           msg += " -- " + channel.URI.spec;
  210.           try {
  211.             msg += ", " + channel.contentType;
  212.           } catch(e1) {}
  213.         }
  214.         if (this.debug) dump(msg + "\n");
  215.       }
  216.     }
  217.   },
  218.   
  219.   onStateChange: function(wp, channel, stateFlag, status) {
  220.     // here we wait STATE_STOP of cached channels
  221.     if ((stateFlag & 16) && (channel instanceof CI.nsICachingChannel))
  222.       this.observe(channel, "http-cached-stop", null);
  223.   }
  224.   /*
  225.   ,
  226.   onLocationChange: function(wp, req, location) {}
  227.   ,
  228.   onLinkIconAvailable: function() {},
  229.   onStatusChange: function() {},
  230.   onSecurityChange: function() {}, 
  231.   onProgressChange: function() {}
  232.   */
  233. };